/*
** srvudp_echo.c
** servidor sem connect()
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>

#define MYPORT 3477    
#define MAXDATASIZE 100
#define TIMEOUT 1

// funcao auxiliar que monitora recvfrom() para lidar com timeouts e pacotes perdidos
int recvfromtimeout(int sockfd, char *buf, int len, struct sockaddr *their_addr, socklen_t *addr_len, int timeout);

int main() {
  int sockfd, new_fd, numbytes; 
  struct sockaddr_in my_addr;    
  struct sockaddr_in their_addr;
  socklen_t addr_len = sizeof(struct sockaddr);
  int sin_size, numreads=0, numchar=0, x=0, yes=1;
  char buf[MAXDATASIZE];
  int terminado=1;

  if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
    perror("socket");
    exit(1);
  }
  
  my_addr.sin_family = AF_INET;        
  my_addr.sin_port = htons(MYPORT);   
  my_addr.sin_addr.s_addr = INADDR_ANY; 
  bzero(&(my_addr.sin_zero), 8);        

  if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
    perror("bind");
    exit(1);
  }

  if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
    perror("setsockopt");
    exit(1);
  }

  while (1) {
    while ((numbytes = recvfromtimeout(sockfd, buf, MAXDATASIZE-1,  
				       (struct sockaddr *)&their_addr, &addr_len, TIMEOUT)) != 0) {
    
      if (numbytes == -1) {
	perror("recvfrom");
	exit(1);
      }

      if (numbytes == -2 && terminado)
	continue;
      
      else if (numbytes == -2) {
	printf("timeout ou pacote perdido\n");
	break;
      }
     
      else 
	terminado = 0;
            
      numreads++;
      numchar += numbytes;

      if ((sendto(sockfd, buf, MAXDATASIZE-1, 0, 
		  (struct sockaddr *)&their_addr, sizeof(struct sockaddr))) == -1) {
	perror("sendto");
	exit(1);
      }
     
    }
        
    fprintf (stderr, "Número de linhas lidas: %d\nNúmero de caracteres lidos: %d\n",
	     numreads, numchar);
    numreads  = 0;
    numchar   = 0;
    terminado = 1;    
  }

  close(sockfd);
  return 0;
}

int recvfromtimeout(int sockfd, char *buf, int len, struct sockaddr *their_addr, socklen_t *addr_len, int timeout) {
  fd_set fds;
  int n;
  struct timeval tv;
  
  FD_ZERO(&fds);
  FD_SET(sockfd, &fds);
  
  tv.tv_sec = timeout;
  tv.tv_usec = 0;
  
  n = select(sockfd+1, &fds, NULL, NULL, &tv);
  if (n == 0) return -2; // timeout!
  if (n == -1) return -1; // error
  
  return recvfrom(sockfd, buf, len, 0, their_addr, addr_len);
}
